Skip to content

Feature/99 use allow multiple times#126

Open
wollomatic wants to merge 14 commits intomainfrom
feature/99-use-allow-multiple-times
Open

Feature/99 use allow multiple times#126
wollomatic wants to merge 14 commits intomainfrom
feature/99-use-allow-multiple-times

Conversation

@wollomatic
Copy link
Copy Markdown
Owner

@wollomatic wollomatic commented Apr 18, 2026

Summary by CodeRabbit

  • New Features

    • Socket-proxy now supports multiple -allow* configuration entries per HTTP method via command-line flags, environment variables, and Docker labels for more granular access control.
  • Documentation

    • Version updated to 1.12.0 with revised examples demonstrating multi-use allow configurations.

@wollomatic wollomatic self-assigned this Apr 18, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 18, 2026

Warning

Rate limit exceeded

@wollomatic has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 42 minutes and 23 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 42 minutes and 23 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dfe38e0a-670c-4010-839f-63844d1f352e

📥 Commits

Reviewing files that changed from the base of the PR and between c62f958 and 9a2e50e.

📒 Files selected for processing (2)
  • README.md
  • cmd/socket-proxy/handlehttprequest.go

Walkthrough

The pull request updates socket-proxy to support multiple allow* configuration entries per HTTP method via command-line flags, environment variables, and Docker labels. The core data structure changes from single-value to multi-value storage, with new parsing modules and handler refactoring to accommodate multiple compiled regexes per HTTP method. Documentation reflects version 1.12.0 release notes.

Changes

Cohort / File(s) Summary
Configuration Core
internal/config/config.go
Updated AllowedRequests from map[string]*regexp.Regexp to map[string][]*regexp.Regexp. Refactored flag parsing from StringVar to multi-value Var, updated environment variable and Docker label processing to aggregate multiple regex values per method, and revised initialization flow to compile and accumulate multiple regexes.
Environment Variable Parsing
internal/config/env.go, internal/config/env_test.go
Added getAllowFromEnv() function to parse multiple SP_ALLOW_* environment variables and map regex strings by HTTP method. Includes table-driven test coverage for single entries, multiple methods, indexed entries, and absent variables.
CLI Flag Parameter Handling
internal/config/param.go
Introduced custom arrayParams type implementing flag.Value interface to support multiple CLI parameter values, with Set() and String() methods for flag parsing and formatting.
Configuration Testing
internal/config/config_test.go
Added unit tests for extractLabelData covering successful regex extraction grouped by HTTP method, invalid regex handling, and filtering of non-allowlist labels.
Request Handler Refactoring
cmd/socket-proxy/handlehttprequest.go
Replaced direct regexp.MatchString() call with new matchURL() helper that iterates over a slice of compiled regexes and returns true when any match the request path.
Documentation
README.md
Updated version tags from 1.11.3 to 1.12.0 and revised examples demonstrating multiple -allowGET/SP_ALLOW_GET entries with distinct values. Extended per-container labels example with additional indexed socket-proxy.allow.head entries. Added changelog entry for multi-use allow* configuration support.

Sequence Diagram(s)

sequenceDiagram
    participant CLI as CLI Flags
    participant ENV as Environment Variables
    participant Labels as Docker Labels
    participant Config as Configuration System
    participant Regexes as Compiled Regexes
    participant Handler as Request Handler

    CLI->>Config: Parse multiple allow* values
    ENV->>Config: getAllowFromEnv() aggregates SP_ALLOW_*
    Labels->>Config: extractLabelData() extracts socket-proxy.allow.*
    
    Config->>Config: Aggregate values by HTTP method
    Config->>Regexes: Compile multiple regexes per method
    
    Handler->>Regexes: matchURL() checks path against all regexes
    Regexes-->>Handler: Return true if any regex matches
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 A socket leaps through config's warren deep,
Now multiple paths each method can keep,
From flags, env, labels all together flow,
Regexes dance in arrays—quite the show!
One hop, two hops, match happens fast,
Socket-proxy's magic spells hold steadfast! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarises the primary change: enabling multiple -allow* configuration entries per HTTP method, which is the main feature introduced across the codebase.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/99-use-allow-multiple-times

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Signed-off-by: Wolfgang Ellsässer <git2026@wollomatic.dev>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/config/config_test.go`:
- Around line 72-97: The test is flaky due to relying on map iteration order
when comparing regex slices; update regexMapsEqual to perform an
order-insensitive comparison: for each entry in map a (method key), fetch
corresponding bRegexes from map b, convert both aRegexes and bRegexes to slices
of strings via ar.String(), sort both string slices (e.g., using sort.Strings),
then compare lengths and each element for equality; keep the function name
regexMapsEqual and ensure extractLabelData tests remain unchanged so the
comparison no longer depends on element ordering.

In `@internal/config/config.go`:
- Around line 164-171: The current merge adds environment regexes into each
mr[i].regexStrings before flag parsing, causing env values to persist even when
a CLI -allow<METHOD> was provided; update the merge logic in the
getAllowFromEnv/initialization block so that for each mr entry (mr[i].method)
you only append env param{value:v, from:fromEnv} if there are no CLI-supplied
values for that method, or alternatively mark env values and let the compile
loop prefer params with source CLI over fromEnv and discard env entries when CLI
entries exist; adjust handling around getAllowFromEnv, mr[i].regexStrings, param
and fromEnv so CLI flags take precedence per method.
- Around line 649-655: Extract the method token from allowSpec using strings.Cut
(as done later) and compare that token exactly against rx.method instead of
using strings.HasPrefix; allow either the exact token (allowSpec == method) or a
dot-suffix (strings.HasPrefix(allowSpec, method+".")). Update the ContainsFunc
predicate that currently uses strings.HasPrefix to first do method, _, _ :=
strings.Cut(allowSpec, ".") and then return method == rx.method, before calling
compileRegexp(labelValue, method, "docker container label") so only true HTTP
methods are accepted and labels like "socket-proxy.allow.getextra" no longer
match.

In `@README.md`:
- Around line 96-101: Update the README examples so repeated-allow env vars are
shown as separate, copy-pasteable shell assignments instead of a single quoted
token; specifically split the ambiguous example into distinct lines like
SP_ALLOW_GET="/v1\..{1,2}/(version|containers/.*|events.*)" and
SP_ALLOW_HEAD=".*" and present the repeated-GET example as two separate
assignments (e.g., SP_ALLOW_GET="/version" and SP_ALLOW_GET_2="/_ping") so users
can copy each variable directly; adjust surrounding text to make clear
SP_ALLOW_GET and SP_ALLOW_GET_2 are separate env vars.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0a266871-4158-4bce-b97c-4742328eba09

📥 Commits

Reviewing files that changed from the base of the PR and between fb6d345 and c62f958.

📒 Files selected for processing (7)
  • README.md
  • cmd/socket-proxy/handlehttprequest.go
  • internal/config/config.go
  • internal/config/config_test.go
  • internal/config/env.go
  • internal/config/env_test.go
  • internal/config/param.go

Comment on lines +72 to +97
if !regexMapsEqual(got, tt.want) {
t.Errorf("extractLabelData() = %v, want %v", got, tt.want)
}
if !reflect.DeepEqual(got2, tt.want2) {
t.Errorf("extractLabelData() = %v, want %v", got2, tt.want2)
}
})
}
}

func regexMapsEqual(a, b map[string][]*regexp.Regexp) bool {
if len(a) != len(b) {
return false
}
for method, aRegexes := range a {
bRegexes, ok := b[method]
if !ok || len(aRegexes) != len(bRegexes) {
return false
}
for i, ar := range aRegexes {
if ar.String() != bRegexes[i].String() {
return false
}
}
}
return true
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

Go specification range over maps iteration order unspecified

💡 Result:

Yes, the Go language specification states that the iteration order over maps using range is unspecified. Maps are defined as unordered groups of elements. The official Go blog "Go maps in action" explicitly confirms: "When iterating over a map with a range loop, the iteration order is not specified and is not guaranteed to be the same from one iteration to the next." Since Go 1.0, the runtime randomizes the iteration order to prevent reliance on any specific order, which could lead to portability issues or bugs if maps are rehashed.

Citations:


🏁 Script executed:

# First, find the extractLabelData function
rg -A 20 "func extractLabelData" internal/config/config.go

Repository: wollomatic/socket-proxy

Length of output: 1092


🏁 Script executed:

# Also examine the test to see how it sets up expected values
rg -B 10 "Test_extractLabelData" internal/config/config_test.go

Repository: wollomatic/socket-proxy

Length of output: 244


🏁 Script executed:

# Check what container.Summary.Labels is
rg -A 5 "type.*Labels" internal/

Repository: wollomatic/socket-proxy

Length of output: 49


🏁 Script executed:

# Get more context on the Test_extractLabelData function and the test cases
rg -A 50 "func Test_extractLabelData" internal/config/config_test.go | head -70

Repository: wollomatic/socket-proxy

Length of output: 1386


🏁 Script executed:

# Search for the test table definition to see how tt.want is constructed
rg "want.*map\[string\]" internal/config/config_test.go -B 5 -A 5

Repository: wollomatic/socket-proxy

Length of output: 995


🏁 Script executed:

# Check if want values are hardcoded or constructed dynamically
rg "tt\.want\s*=" internal/config/config_test.go -B 3 -A 3

Repository: wollomatic/socket-proxy

Length of output: 49


🏁 Script executed:

# Check the full extractLabelData function to see if there's any sorting
rg -A 40 "func extractLabelData" internal/config/config.go

Repository: wollomatic/socket-proxy

Length of output: 1186


🏁 Script executed:

# Check if there are any other tests or assertions that might handle ordering
rg "regexMapsEqual\|reflect.DeepEqual" internal/config/config_test.go -B 3 -A 1

Repository: wollomatic/socket-proxy

Length of output: 49


Fix test flakiness caused by unspecified map iteration order.

The test input uses a map for Docker labels, which has unspecified iteration order in Go. The extractLabelData function iterates over cntr.Labels and appends regexes to slices in the order encountered. With positional comparison in regexMapsEqual, the test can fail randomly when labels are iterated in a different order than expected—for example, "socket-proxy.allow.get.1" before "socket-proxy.allow.get.0" would produce regexes in a different sequence.

Replace the positional comparison with order-insensitive comparison by sorting regex strings before comparing.

🧪 Proposed order-insensitive comparison
 import (
 	"reflect"
 	"regexp"
+	"slices"
 	"testing"
 
 	"github.com/wollomatic/socket-proxy/internal/docker/api/types/container"
 )
@@
 func regexMapsEqual(a, b map[string][]*regexp.Regexp) bool {
 	if len(a) != len(b) {
 		return false
 	}
 	for method, aRegexes := range a {
 		bRegexes, ok := b[method]
 		if !ok || len(aRegexes) != len(bRegexes) {
 			return false
 		}
-		for i, ar := range aRegexes {
-			if ar.String() != bRegexes[i].String() {
-				return false
-			}
+		aStrings := regexStrings(aRegexes)
+		bStrings := regexStrings(bRegexes)
+		slices.Sort(aStrings)
+		slices.Sort(bStrings)
+		if !slices.Equal(aStrings, bStrings) {
+			return false
 		}
 	}
 	return true
 }
+
+func regexStrings(regexes []*regexp.Regexp) []string {
+	values := make([]string, 0, len(regexes))
+	for _, rx := range regexes {
+		values = append(values, rx.String())
+	}
+	return values
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if !regexMapsEqual(got, tt.want) {
t.Errorf("extractLabelData() = %v, want %v", got, tt.want)
}
if !reflect.DeepEqual(got2, tt.want2) {
t.Errorf("extractLabelData() = %v, want %v", got2, tt.want2)
}
})
}
}
func regexMapsEqual(a, b map[string][]*regexp.Regexp) bool {
if len(a) != len(b) {
return false
}
for method, aRegexes := range a {
bRegexes, ok := b[method]
if !ok || len(aRegexes) != len(bRegexes) {
return false
}
for i, ar := range aRegexes {
if ar.String() != bRegexes[i].String() {
return false
}
}
}
return true
func regexMapsEqual(a, b map[string][]*regexp.Regexp) bool {
if len(a) != len(b) {
return false
}
for method, aRegexes := range a {
bRegexes, ok := b[method]
if !ok || len(aRegexes) != len(bRegexes) {
return false
}
aStrings := regexStrings(aRegexes)
bStrings := regexStrings(bRegexes)
slices.Sort(aStrings)
slices.Sort(bStrings)
if !slices.Equal(aStrings, bStrings) {
return false
}
}
return true
}
func regexStrings(regexes []*regexp.Regexp) []string {
values := make([]string, 0, len(regexes))
for _, rx := range regexes {
values = append(values, rx.String())
}
return values
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/config/config_test.go` around lines 72 - 97, The test is flaky due
to relying on map iteration order when comparing regex slices; update
regexMapsEqual to perform an order-insensitive comparison: for each entry in map
a (method key), fetch corresponding bRegexes from map b, convert both aRegexes
and bRegexes to slices of strings via ar.String(), sort both string slices
(e.g., using sort.Strings), then compare lengths and each element for equality;
keep the function name regexMapsEqual and ensure extractLabelData tests remain
unchanged so the comparison no longer depends on element ordering.

Comment thread internal/config/config.go
Comment on lines +164 to +171
// multiple values per method
// like SP_ALLOW_GET_0, SP_ALLOW_GET_1, ...
allowFromEnv := getAllowFromEnv(os.Environ())
for i := range mr {
if val, ok := os.LookupEnv("SP_ALLOW_" + mr[i].method); ok && val != "" {
mr[i].regexStringFromEnv = val
if val, ok := allowFromEnv[mr[i].method]; ok && len(val) > 0 {
for _, v := range val {
mr[i].regexStrings = append(mr[i].regexStrings, param{value: v, from: fromEnv})
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Preserve CLI-over-env precedence per method.

Env regexes are appended before flag parsing, then the compile loop includes both env and CLI values. That contradicts the documented behaviour and can keep a broader SP_ALLOW_* active even when a restrictive -allow<METHOD> flag is provided.

🔒 Proposed precedence fix
 	// compile regexes for default allowed requests
 	cfg.AllowLists.Default.AllowedRequests = make(map[string][]*regexp.Regexp)
 	for _, rx := range mr {
+		hasParamValue := slices.ContainsFunc(rx.regexStrings, func(regexString param) bool {
+			return regexString.from == fromParam
+		})
 		for _, regexString := range rx.regexStrings {
+			if hasParamValue && regexString.from == fromEnv {
+				continue
+			}
 			if regexString.value != "" {
 				location := ""
 				switch regexString.from {
 				case fromEnv:
 					location = "env variable"

Also applies to: 253-266

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/config/config.go` around lines 164 - 171, The current merge adds
environment regexes into each mr[i].regexStrings before flag parsing, causing
env values to persist even when a CLI -allow<METHOD> was provided; update the
merge logic in the getAllowFromEnv/initialization block so that for each mr
entry (mr[i].method) you only append env param{value:v, from:fromEnv} if there
are no CLI-supplied values for that method, or alternatively mark env values and
let the compile loop prefer params with source CLI over fromEnv and discard env
entries when CLI entries exist; adjust handling around getAllowFromEnv,
mr[i].regexStrings, param and fromEnv so CLI flags take precedence per method.

Comment thread internal/config/config.go
Comment on lines +649 to +655
if slices.ContainsFunc(mr, func(rx methodRegex) bool {
// allowSpec starts with the method name like socket-proxy.allow.get.1
return strings.HasPrefix(allowSpec, rx.method)
}) {
// extract the method name from allowSpec
method, _, _ := strings.Cut(allowSpec, ".")
r, err := compileRegexp(labelValue, method, "docker container label")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Require an exact method label or method-plus-dot suffix.

HasPrefix also accepts labels such as socket-proxy.allow.getextra, producing a GETEXTRA allow entry. Parse the method first, then compare it exactly against supported HTTP methods.

🛡️ Proposed stricter label parsing
-			if slices.ContainsFunc(mr, func(rx methodRegex) bool {
-				// allowSpec starts with the method name like  socket-proxy.allow.get.1
-				return strings.HasPrefix(allowSpec, rx.method)
-			}) {
-				// extract the method name from allowSpec
-				method, _, _ := strings.Cut(allowSpec, ".")
+			method, _, _ := strings.Cut(allowSpec, ".")
+			if slices.ContainsFunc(mr, func(rx methodRegex) bool {
+				return method == rx.method
+			}) {
 				r, err := compileRegexp(labelValue, method, "docker container label")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/config/config.go` around lines 649 - 655, Extract the method token
from allowSpec using strings.Cut (as done later) and compare that token exactly
against rx.method instead of using strings.HasPrefix; allow either the exact
token (allowSpec == method) or a dot-suffix (strings.HasPrefix(allowSpec,
method+".")). Update the ContainsFunc predicate that currently uses
strings.HasPrefix to first do method, _, _ := strings.Cut(allowSpec, ".") and
then return method == rx.method, before calling compileRegexp(labelValue,
method, "docker container label") so only true HTTP methods are accepted and
labels like "socket-proxy.allow.getextra" no longer match.

Comment thread README.md Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants